package controllers

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"sync"

	"github.com/golang/glog"
	"github.com/gorilla/mux"

	authapi "eccgateway/pkg/api/auth"
	apibatch "eccgateway/pkg/api/batch"
	"eccgateway/pkg/controllers/parse"
	authsvc "eccgateway/pkg/services/auth"
	servicebatch "eccgateway/pkg/services/batch"
	"eccgateway/pkg/util"
)

type BatchController struct {
	service servicebatch.BatchService
}

var batchController BatchController
var once sync.Once

func GetBatchController() BatchController {
	once.Do(func() {
		batchController = BatchController{service: servicebatch.NewBatchService()}
	})
	return batchController
}

func (c BatchController) Create(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	requestBody, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorf("request body read failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("request body read failed, %v", err))
		return
	}

	req := apibatch.CreateBatchWorkReq{}
	err = json.Unmarshal(requestBody, &req)
	if err != nil {
		glog.Errorf("unmarshal request body failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("unmarshal request body failed, %v", err))
		return
	}

	err = validCreateReqBody(req)
	if err != nil {
		glog.Errorf("create request body is invalid, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, fmt.Sprintf("%v", err))
		return
	}

	resp, err := c.service.Create(*info, req)
	if err != nil {
		glog.Errorf("create batch work failed, %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("create batch work failed, %v", err))
		return
	}

	respBody, _ := json.Marshal(resp)
	w.WriteHeader(http.StatusCreated)
	_, _ = w.Write(respBody)
}

func (c BatchController) List(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	dataSelect := parse.ParseDataSelectPathParameter(r)

	resp, nums, err := c.service.ListBatchWorkByUserID(authsvc.GetManageUserIDs(info), dataSelect)
	if err != nil {
		glog.Errorf("list batch work failed, user id: %v, %v", info.UserID, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	respBody, _ := json.Marshal(apibatch.List{
		Count: nums,
		Item:  resp,
	})
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func (c BatchController) Get(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	batchWorkName := mux.Vars(r)["name"]
	resp, err := c.service.GetBatchWorkByUserIDAndName(info.UserID, batchWorkName)
	if err != nil {
		glog.Errorf("get batch work[%v] failed, %v", batchWorkName, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	respBody, _ := json.Marshal(resp)
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func (c BatchController) Stop(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	batchWorkName := mux.Vars(r)["name"]
	resp, err := c.service.StopBatchWork(info.UserID, batchWorkName)
	if err != nil {
		glog.Errorf("stop batch work[%v] failed, %v", batchWorkName, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	respBody, _ := json.Marshal(resp)
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func (c BatchController) Start(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	batchWorkName := mux.Vars(r)["name"]
	resp, err := c.service.StartBatchWork(info.UserID, batchWorkName)
	if err != nil {
		glog.Errorf("start batch work[%v] failed, %v", batchWorkName, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	respBody, _ := json.Marshal(resp)
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func (c BatchController) Delete(w http.ResponseWriter, r *http.Request) {
	info, err := util.GetUserInfo(r.Header.Get(authapi.ParseInfo))
	if err != nil {
		glog.Errorf("get user info from header failed, err: %v", err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusBadRequest, err.Error())
		return
	}

	batchWorkName := mux.Vars(r)["name"]
	resp, err := c.service.DeleteBatchWork(info.UserID, batchWorkName)
	if err != nil {
		glog.Errorf("delete batch work[%v] failed, %v", batchWorkName, err)
		util.ReturnErrorResponseInResponseWriter(w, http.StatusInternalServerError, fmt.Sprintf("%v", err))
		return
	}

	respBody, _ := json.Marshal(resp)
	w.WriteHeader(http.StatusOK)
	_, _ = w.Write(respBody)
}

func validCreateReqBody(req apibatch.CreateBatchWorkReq) error {
	if len(req.Name) == 0 {
		return fmt.Errorf("batch name can't be empty")
	}

	match, _ := regexp.MatchString(apibatch.NamePattern, req.Name)
	if !match {
		return fmt.Errorf("batch work name \"%v\" is invalid, regex used for validation is '%v'", req.Name, apibatch.NamePattern)
	}

	typeIsExist := false
	for _, v := range apibatch.AllowBatchWorkType {
		if req.Type == v {
			typeIsExist = true
			break
		}
	}
	if !typeIsExist {
		return fmt.Errorf("type is invalid")
	}

	if len(req.List) == 0 {
		return fmt.Errorf("list is empty")
	}

	for _, v := range req.List {
		if len(v.Cluster) == 0 {
			return fmt.Errorf("cluster \"%v\" is empty", v.Cluster)
		}
	}

	_, err := servicebatch.TransformTemplate2Deployment(req.Template)
	if err == servicebatch.TemplateIsEmptyError {
		return servicebatch.TemplateIsEmptyError
	}
	if err != nil {
		glog.Infof("template isn't deployment, %v", err)
		return fmt.Errorf("template isn't deployment")
	}
	return nil
}
